package com.feishi.core.media.javacv;


import com.feishi.core.media.javacv.util.JavaCvUtil;
import com.feishi.core.util.queue.Producer;
import com.feishi.core.util.queue.feishi.FeishiQueueUtil;
import com.feishi.core.util.queue.feishi.Msg;
import org.bytedeco.javacv.*;

import javax.sound.sampled.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import static org.bytedeco.javacpp.FlyCapture2.FRAME_RATE;

/**
 * @version 1.0
 * @auth caixq
 * @time 18-5-7 16:31
 **/
public class DeviceReader {
    private static int height;
    private static int width;
    private static OpenCVFrameGrabber grabber;
    private static ExecutorService pool=Executors.newCachedThreadPool();
    /**
     * 定时器，用于定时采样音频信息
     * */

    private static FFmpegFrameRecorder recorder;

    private static Map<String,Producer<Msg<Integer,Frame>>> producerMap=new HashMap<>();

    public static void startReadCamera(){
        grabber = new OpenCVFrameGrabber(0);
        boolean openCamera=JavaCvUtil.openCamera(grabber);
        if(!openCamera){
            System.out.println("open camera failed");
        }
        height=grabber.getImageHeight();
        width=grabber.getImageWidth();

        pool.execute(()->{
            Frame capturedFrame = null;

            while (true){
                try {
                    capturedFrame = grabber.grab();
                    Msg msg=new Msg(0,capturedFrame);

                    for (Producer<Msg<Integer, Frame>> feishiProducer : producerMap.values()) {
                        feishiProducer.push(msg);
                    }
                } catch (FrameGrabber.Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }


    public static void startRecord(String filePath,long recordLength) {

        recorder=JavaCvUtil.createFrameRecorder(filePath, width, height);
        boolean openRecorder=JavaCvUtil.openRecorder(recorder);
        if(!openRecorder){
            System.err.println("err: openRecorder failed");
        }
        final long[] startTime = {0};
        producerMap.put("record",FeishiQueueUtil.getProductor((msg, sequence, endOfBatch)->{
            if(startTime[0] ==0){
                startTime[0] =System.currentTimeMillis();
            }
            long videoTS = 1000 * (System.currentTimeMillis() - startTime[0]);
            if (videoTS > recorder.getTimestamp()) {
                //告诉录制器写入这个timestamp
                recorder.setTimestamp(videoTS);
                //在此时间戳上插入一帧
                recorder.record(msg.getBody());
            }
        }));
        AtomicBoolean isStop= new AtomicBoolean(false);


        recordAudio(recorder,isStop);
//        DeviceReaderTest.catchVoice();





        pool.execute(()->{
            try {
                Thread.sleep(recordLength);
                /**
                 * 定时清理任务
                 * */
                producerMap.remove("record");
                isStop.set(true);
                JavaCvUtil.closeRecord(recorder);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

    }

    private static void recordAudio(FFmpegFrameRecorder recorder, AtomicBoolean isStop) {

        ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);


        AudioFormat audioFormat = new AudioFormat(44100.0F, 16, 2, true, false);
        TargetDataLine line = JavaCvUtil.getDataLine(audioFormat);
        line.start();
        int sampleRate = (int) audioFormat.getSampleRate();
        int numChannels = audioFormat.getChannels();
        byte[] audioBytes = new byte[sampleRate * numChannels];
        exec.scheduleAtFixedRate(() -> {

            try {
                if(isStop.get()){
                    line.stop();
                    exec.shutdown();
                    return;
                }
                System.out.println(line.available());
                int nBytesRead = line.read(audioBytes, 0, line.available());
                short[] samples = new short[nBytesRead/2];
                /**
                 * 拷贝数据到新数据
                 * */
                ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples);
                ShortBuffer sBuff = ShortBuffer.wrap(samples);
                recorder.recordSamples(sampleRate, numChannels, sBuff);
            } catch (FrameRecorder.Exception e) {
                e.printStackTrace();
            }
        }, 0, (long) 1000 / FRAME_RATE, TimeUnit.MILLISECONDS);
    }
}
